
/*
 *                   ___====-_  _-====___
 *             _--^^^#####//      \\#####^^^--_
 *          _-^##########// (    ) \\##########^-_
 *         -############//  |\^^/|  \\############-
 *       _/############//   (@::@)   \############\_
 *      /#############((     \\//     ))#############\
 *     -###############\\    (oo)    //###############-
 *    -#################\\  / VV \  //#################-
 *   -###################\\/      \//###################-
 *  _#/|##########/\######(   /\   )######/\##########|\#_
 *  |/ |#/\#/\#/\/  \#/\##\  |  |  /##/\#/  \/\#/\#/\#| \|
 *  `  |/  V  V  `   V  \#\| |  | |/#/  V   '  V  V  \|  '
 *     `   `  `      `   / | |  | | \   '      '  '   '
 *                      (  | |  | |  )
 *                     __\ | |  | | /__
 *                    (vvv(VVV)(VVV)vvv)
 * 
 *      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * 
 *                神兽保佑            永无BUG
 */

/*
 * @Author: 火红色祥云 809880192@qq.com
 * @Date: 2022-06-19 17:11:39
 * @LastEditors: 火红色祥云 809880192@qq.com
 * @LastEditTime: 2022-10-06 16:14:17
 * @FilePath: /imx6ull-app/09_jiffy_timer/jiffytimer.c
 * @Description: 用于学习使用Linux中的软件定时器
 */
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_address.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/fs.h>

#define CLOSE_CMD       (_IO(0XEF,0x01))
#define OPEN_CMD        (_IO(0XEF,0x02))
#define SETPERIOD_CMD   (_IO(0XEF,0x03))

/**************************************************
* Copyright (c) 2022-2030, Lux.Co.Ltd
* filename:
* function:
* auther  :Lux
* Date    :2022-10-6
* Version :V1.0
**************************************************/
struct ST_JIFFY{

    struct device_node *pst_node;
    int gpio_num;

    dev_t devid;
    int major;
    int minor;
    struct cdev st_cdev;
    struct class *pst_class;
    struct device *pst_device;

    int timeperiod;
    struct timer_list st_timer;

    spinlock_t  lock;
};

struct ST_JIFFY st_jiffy;

/**************************************************
* Copyright (c) 2022-2030, Lux.Co.Ltd
* filename:LedInit
* function:从设备树中找出GPIO节点信息并申请GPIO
* param   :无
* return  :
* auther  :Lux
* Date    :2022-10-6
* Version :V1.0
**************************************************/
int LedInit(void)
{
    //查找节点
    st_jiffy.pst_node = of_find_node_by_path("/lux_ledgpio");
    if (st_jiffy.pst_node == NULL)
    {
        printk("lux_ledgpio node  not  find\r\n");
        return -1;
    }
    
    //查询gpio id
    st_jiffy.gpio_num = of_get_named_gpio(st_jiffy.pst_node,"led-gpio",0);
    if (st_jiffy.gpio_num < 0)
    {
        printk("gpio_num is not find\\n");
        return -1;
    }
    
    //设置gpio为输出
    gpio_request(st_jiffy.gpio_num,"led-gpio");
    gpio_direction_output(st_jiffy.gpio_num,1);
    return 0;
}

/**************************************************
* Copyright (c) 2022-2030, Lux.Co.Ltd
* filename: timer_function
* function: 定时器的中断服务函数，当定时周期满时调用
* param   : arg
* return  : 无
* auther  :Lux
* Date    :2022-10-6
* Version :V1.0
**************************************************/
void timer_function(unsigned long arg)
{
    struct ST_JIFFY *st_tfn = (struct ST_JIFFY *)arg;

    static int sta = 1;

    int timerperiod;

    unsigned long flags;

    sta = !sta;

    gpio_set_value(st_tfn->gpio_num,sta);

    //重启定时器
    spin_lock_irqsave(&st_tfn->lock,flags);
    timerperiod = st_tfn->timeperiod;
    spin_unlock_irqrestore(&st_tfn->lock,flags);
    mod_timer(&st_tfn->st_timer,jiffies+msecs_to_jiffies(st_tfn->timeperiod));

}
/**************************************************
* Copyright (c) 2022-2030, Lux.Co.Ltd
* filename: open_jiffy
* function: Open函数
* param   : 
* return  : 
* auther  : Lux
* Date    : 2022-10-6
* Version : V1.0
**************************************************/
int open_jiffy(struct inode *nd, struct file *fp)
{
    int retval = 0;
    retval = LedInit();
    if (retval < 0)
    {
        printk("LedInit failed\r\n");
        return -1;
    }
    st_jiffy.timeperiod = 1000;
    fp->private_data = &st_jiffy;
    return 0;
}

/**************************************************
* Copyright (c) 2022-2030, Lux.Co.Ltd
* filename: ioctl_jiffy
* function: 定时器的IO控制函数
* param   : 
* return  : 
* auther  : Lux
* Date    : 2022-10-6
* Version : V1.0
**************************************************/
long ioctl_jiffy(struct file *fp, unsigned int cmd, unsigned long arg)
{
    struct ST_JIFFY *st_private = fp->private_data;

    int timerperiod = 0;

    unsigned long flags = 0;

    switch (cmd)
    {
    case CLOSE_CMD:
    {
        //int del_timer(struct timer_list * timer);
        del_timer_sync(&st_private->st_timer);
    }break;    
    case OPEN_CMD:
    {
        spin_lock_irqsave(&st_private->lock,flags);
        timerperiod = st_private->timeperiod;
        spin_unlock_irqrestore(&st_private->lock,flags);
        //int mod_timer(struct timer_list *timer, unsigned long expires);
        mod_timer(&st_private->st_timer,jiffies+msecs_to_jiffies(timerperiod));
    }break;
    case SETPERIOD_CMD:
    {
        spin_lock_irqsave(&st_private->lock,flags);
        st_private->timeperiod = arg;
        spin_unlock_irqrestore(&st_private->lock,flags);
        //int mod_timer(struct timer_list *timer, unsigned long expires);
        mod_timer(&st_private->st_timer,jiffies+msecs_to_jiffies(arg));
    }break;
    default:
        break;
    }

    return 0;
}

struct file_operations fops_jiffy = {
    .owner = THIS_MODULE,
    .open = open_jiffy,
    .unlocked_ioctl = ioctl_jiffy,
};

/**************************************************
* Copyright (c) 2022-2030, Lux.Co.Ltd
* filename: jiffy_init
* function: 字符设备注册
* param   : 
* return  : 
* auther  : Lux
* Date    : 2022-10-6
* Version : V1.0
**************************************************/
static int __init jiffy_init(void)
{

    alloc_chrdev_region(&st_jiffy.devid,0,1,"timer_jiffy");

    st_jiffy.st_cdev.owner = THIS_MODULE;
    cdev_init(&st_jiffy.st_cdev,&fops_jiffy);
    cdev_add(&st_jiffy.st_cdev,st_jiffy.devid,1);

    st_jiffy.pst_class = class_create(THIS_MODULE,"timer_jiffy");
    st_jiffy.pst_device = device_create(st_jiffy.pst_class,NULL,st_jiffy.devid,NULL,"timer_jiffy");

    //struct timer_list timer;
    init_timer(&st_jiffy.st_timer);
    st_jiffy.st_timer.function = timer_function;
    st_jiffy.st_timer.data = (unsigned long)&st_jiffy;
    return 0;
}

/**************************************************
* Copyright (c) 2022-2030, Lux.Co.Ltd
* filename: jiffy_exit
* function: 字符设备注销
* param   : 
* return  : 
* auther  : Lux
* Date    : 2022-10-6
* Version : V1.0
**************************************************/
static void __exit jiffy_exit(void)
{
    gpio_set_value(st_jiffy.gpio_num,1);
    del_timer_sync(&st_jiffy.st_timer);
    unregister_chrdev_region(st_jiffy.devid,1);
    cdev_del(&st_jiffy.st_cdev);
    device_destroy(st_jiffy.pst_class,st_jiffy.devid);
    class_destroy(st_jiffy.pst_class);
}

module_init(jiffy_init);
module_exit(jiffy_exit);


MODULE_LICENSE("GPL");
MODULE_AUTHOR("Lux");
